/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "ApiOperator.h"
#include <chrono>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include "error.h"
#include "system/System.hpp"
#include "common/utils/Utils.h"
#include "common/Utils.h"
#include "common/JsonUtils.h"
#include "common/Structs.h"
#include "common/Timer.h"
#include "common/Constants.h"
#include "DiskScannerHandler.h"
#include "ApiOperatorManager.h"
#include "volume_handlers/fusionstorage/FusionStorageIscsiDiskScanner.h"

using namespace chrono;
namespace {
const std::string MODULE_NAME = "ApiOperator";
const int32_t HOST_TYPE = 21;
const int32_t LINUX_TYPE = 0;
const int32_t DOS_REQ_INTERVAL = 10;
const int32_t DOS_REQ_COUNT = 100;
const int32_t MAX_LENGTH = 31;
const int32_t ORIGIN_TIMEOUT = 30;
const int NUM_100 = 100;
const std::string ISCSI_SESSION_STATE_LOGGED_IN = "LOGGED IN";    // iscsi会话正常连接状态
using Defer = std::shared_ptr<void>;
}

namespace VirtPlugin {
std::unique_ptr<SessionCache> ApiOperator::m_sessionCache = std::make_unique<SessionCache>(MODULE_NAME);
std::mutex ApiOperator::m_mutexMappingView;
std::mutex ApiOperator::m_mutexHost;
std::mutex ApiOperator::m_mutexLunGroup;
std::mutex ApiOperator::m_mutexHostGroup;

ApiOperator::~ApiOperator()
{
    ClearCacheSession();
}

void ApiOperator::ClearCacheSession()
{
    if (m_useCache && m_sessionCache != nullptr) {
        if (m_sessionPtr != nullptr) {
            DeleteDeviceSession();
        }
    } else {
        if (m_sessionPtr != nullptr) {
            StorageSessionInfo sessionInfo;
            sessionInfo.token = m_sessionPtr->token;
            sessionInfo.cookie = m_sessionPtr->cookie;
            sessionInfo.deviceId = m_sessionPtr->deviceId;
            Logout(sessionInfo);
        }
    }
}

void ApiOperator::SetDeviceInfo(const ControlDeviceInfo &info)
{
    std::lock_guard<std::mutex> guard(m_mutexSetInfo);
    m_deviceInfo.m_ip = info.m_ip;
    m_deviceInfo.m_port = info.m_port;
    m_deviceInfo.m_userName = info.m_userName;
    m_deviceInfo.m_password = info.m_password;
    m_deviceInfo.m_enableCert = info.m_enableCert;
    m_deviceInfo.m_poolId = info.m_poolId;
    m_deviceInfo.m_cert = info.m_cert;
}

StorageSessionInfo ApiOperator::Login(std::string &errorDes)
{
    INFOLOG("Start authentication login.");
    Module::HttpRequest req;
    req.method = "POST";
    req.url = "sessions";
    Json::Value jsonValue;
    Json::FastWriter jsonWriter;
    jsonValue["username"] = m_deviceInfo.m_userName;
    jsonValue["password"] = m_deviceInfo.m_password;
    jsonValue["scope"] = "0";
    req.body = jsonWriter.write(jsonValue);
    Json::Value data;
    StorageSessionInfo sessionInfo {};
    StorageSessionInfo retSessionInfo {};
    int errorCode = Module::FAILED;
    for (auto &ip : m_deviceInfo.m_ipList) {
        sessionInfo.ip = ip;
        if (SendRequestEx(req, data, errorDes, errorCode, sessionInfo) == Module::SUCCESS &&
            errorCode == Module::SUCCESS) {
            if (data.isMember("deviceid") && data.isMember("iBaseToken")) {
                sessionInfo.token = data["iBaseToken"].asString();
                sessionInfo.deviceId = data["deviceid"].asString();
            }
            retSessionInfo = sessionInfo;
            if (m_isRetry) {
                INFOLOG("Login storage success, ip :%s", ip.c_str());
                break;
            }
            if (!retSessionInfo.cookie.empty()) {
                Logout(retSessionInfo);
            }
        } else {
            ERRLOG("Login storage failed, ip: %s, errorDes: %s", ip.c_str(), errorDes.c_str());
            if (!m_isRetry) {
                errorDes = ip;
                break;
            }
        }
        for (std::string::iterator it = req.body.begin(); it != req.body.end(); ++it) {
            *it = 0;
        }
    }
    for (std::string::iterator it = req.body.begin(); it != req.body.end(); ++it) {
        *it = 0;
    }
    INFOLOG("Login storage success");
    return retSessionInfo;
}

StorageSessionInfo ApiOperator::LoginForIp(std::string &errorDes, const std::string &ip)
{
    INFOLOG("Start authentication login.");
    Module::HttpRequest req;
    req.method = "POST";
    req.url = "sessions";
    Json::Value jsonValue;
    Json::FastWriter jsonWriter;
    jsonValue["username"] = m_deviceInfo.m_userName;
    jsonValue["password"] = m_deviceInfo.m_password;
    jsonValue["scope"] = "0";
    req.body = jsonWriter.write(jsonValue);
    Json::Value data;
    StorageSessionInfo sessionInfo {};
    int errorCode = Module::FAILED;
    sessionInfo.ip = ip;
    if (SendRequestEx(req, data, errorDes, errorCode, sessionInfo) == Module::SUCCESS && errorCode == Module::SUCCESS) {
        if (data.isMember("deviceid") && data.isMember("iBaseToken")) {
            sessionInfo.token = data["iBaseToken"].asString();
            sessionInfo.deviceId = data["deviceid"].asString();
        }
    } else {
        ERRLOG("Login storage failed, ip: %s, errorDes: %s", ip.c_str(), errorDes.c_str());
        if (!m_isRetry) {
            errorDes = ip;
        }
    }
    for (std::string::iterator it = req.body.begin(); it != req.body.end(); ++it) {
        *it = 0;
    }
    INFOLOG("Login storage success");
    return sessionInfo;
}

int32_t ApiOperator::Logout(StorageSessionInfo sessionInfo)
{
    INFOLOG("Start authentication logout. ");
    Module::HttpRequest req;
    Json::Value jsonReq;
    req.method = "DELETE";
    req.url = "sessions";
    Json::Value data;
    std::string errorDes;
    int errorCode = Module::FAILED;
    INFOLOG("Lopgout ip: %s", sessionInfo.ip.c_str());
    if (SendRequestEx(req, data, errorDes, errorCode, sessionInfo) == Module::SUCCESS && errorCode == Module::SUCCESS) {
        return Module::SUCCESS;
    }
    ERRLOG("Logout failed, error code:%d, error des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

void ApiOperator::DosAttackDefense()
{
    if (CheckDosAttack()) {
        int iDosReqInterval = Module::ConfigReader::getInt("BackupNode", "DosRequestInterval");
        if (iDosReqInterval == 0) {
            iDosReqInterval = DOS_REQ_INTERVAL;
        }
        WARNLOG("Sleep %d seconds, avoid generate dos attack", iDosReqInterval);
        std::this_thread::sleep_for(std::chrono::seconds(iDosReqInterval));
    }
}

void ApiOperator::CheckIpNotEmpty(std::string &ip)
{
    if (ip.empty()) {
        ip = m_deviceInfo.m_ip;
    }
}

int32_t ApiOperator::SendRequestEx(Module::HttpRequest &req, Json::Value &data, std::string &errorDes,
                                   int &errorCode, StorageSessionInfo &sessionInfo)
{
    DosAttackDefense();
    Module::HttpRequest request = req;
    req.isVerify = Module::CACertVerification::DO_NOT_VERIFY;
    if (req.method == "DELETE") {
        req.url = m_curlHttp + sessionInfo.ip + ":" + m_deviceInfo.m_port + "/deviceManager/rest/" +
            sessionInfo.deviceId + "/" + req.url;
    } else {
        req.url = m_curlHttp + sessionInfo.ip + ":" + m_deviceInfo.m_port + "/deviceManager/rest" + "//" + req.url;
    }
    (void)req.heads.insert(std::make_pair(std::string("Cookie"), sessionInfo.cookie));
    (void)req.heads.insert(std::make_pair(std::string("iBaseToken"), sessionInfo.token));
    req.url = FormatFullUrl(req.url);
    Utils::InnerAgentAppointNetDevice(req, m_isOpService);
    if (m_httpClient == nullptr) {
        ERRLOG("Http client is empty.");
        return Module::FAILED;
    }
    std::shared_ptr<Module::IHttpResponse> rsp = m_httpClient->SendMemCertRequest(req, ORIGIN_TIMEOUT);
    if (rsp.get() == nullptr) {
        ERRLOG("Return response is empty.");
        return Module::FAILED;
    }
    if (!rsp->Success()) {
        if (rsp->GetErrCode() != 0) {
            errorDes = rsp->GetHttpStatusDescribe();
            DBGLOG("Http response error, http status des:%s", errorDes.c_str());
        } else {
            errorDes = rsp->GetErrString();
            DBGLOG("Send http request occur network error, error des:%s", errorDes.c_str());
        }
        return Module::FAILED;
    } else {
        if (req.method == "DELETE") {
            return ParseResponse(rsp->GetBody(), data, errorDes, errorCode);
        }
        int iRet = ParseCookie(rsp->GetCookies(), sessionInfo);
        if (iRet != Module::SUCCESS) {
            return iRet;
        }
        return ParseResponse(rsp->GetBody(), data, errorDes, errorCode);
    }
}

int32_t ApiOperator::CheckResponse(const std::shared_ptr<Module::IHttpResponse> rsp, Json::Value &data,
    std::string &errorDes)
{
    if (rsp.get() == nullptr) {
        ERRLOG("Return response is empty.");
        return Module::FAILED;
    }
    if (!rsp->Success()) {
        if (rsp->GetErrCode() != 0) {
            errorDes = rsp->GetHttpStatusDescribe();
            DBGLOG("Http response error, http status des:%s", errorDes.c_str());
        } else {
            errorDes = rsp->GetErrString();
            DBGLOG("Send http request occur network error, error des:%s", errorDes.c_str());
        }
        return Module::FAILED;
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::ParseResponse(const std::string &body, Json::Value &data, std::string &errorDes, int &errorCode)
{
    Json::Value jsonValue;
    Json::Reader reader;
    if (!reader.parse(body, jsonValue)) {
        errorDes = "Parse json string failed";
        ERRLOG("Parse response failed, err des: %s.", WIPE_SENSITIVE(body).c_str());
        return Module::FAILED;
    }
    if (!jsonValue.isMember("error") || !jsonValue["error"].isMember("code") ||
        !jsonValue["error"].isMember("description")) {
        errorDes = "Json object format is error.";
        ERRLOG("Parse response failed, err des: %s.", WIPE_SENSITIVE(body).c_str());
        return Module::FAILED;
    }
    int32_t rspErrorCode = jsonValue["error"]["code"].asInt();
    if (rspErrorCode != Module::SUCCESS && IsNeedRetryErrorCode(rspErrorCode)) {
        WARNLOG("Error code:%d, describe:%s", jsonValue["error"]["code"].asInt(),
            jsonValue["error"]["description"].asString().c_str());
        errorDes = jsonValue["error"]["description"].asString();
        errorCode = jsonValue["error"]["code"].asInt();
        return Module::FAILED;
    }
    if (jsonValue.isMember("data")) {
        data = jsonValue["data"];
        errorDes = jsonValue["error"]["description"].asString();
        errorCode = jsonValue["error"]["code"].asInt();
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::ParseCookie(const std::set<std::string> &cookieValues, StorageSessionInfo &sessionInfo)
{
    if (cookieValues.empty()) {
        ERRLOG("Parse cookie failed, cookie is empty.");
        return Module::FAILED;
    }
    std::string cookieValue = *cookieValues.begin();
    std::vector<std::string> strs;
    (void)boost::split(strs, cookieValue, boost::is_any_of(";"));
    if (strs.size() > 0) {
        sessionInfo.cookie = strs[0];
        return Module::SUCCESS;
    }
    ERRLOG("Split Cookie failed.");
    return Module::FAILED;
}

int32_t ApiOperator::SendHttpReq(
    std::shared_ptr<Module::IHttpResponse> &rsp, const Module::HttpRequest &req, std::string &errorDes, int& errorCode)
{
    Module::HttpRequest tempReq = req;
    tempReq.isVerify = req.isVerify;
    tempReq.cert = req.cert;
    tempReq.revocationList = req.revocationList;
    tempReq.url = FormatFullUrl(tempReq.url);
    Utils::InnerAgentAppointNetDevice(tempReq, m_isOpService);
    if (m_httpClient == nullptr) {
        ERRLOG("Http client is empty.");
        return Module::FAILED;
    }
    rsp = m_httpClient->SendRequest(tempReq, m_curlTimeOut);
    if (rsp.get() == nullptr) {
        ERRLOG("Return response is empty.");
        return Module::FAILED;
    }
    if (!rsp->Success()) {
        // 1.curl success,http response error with http status codes
        if (rsp->GetErrCode() == 0) {
            errorDes = rsp->GetHttpStatusDescribe(); // http status error description
            errorCode = rsp->GetErrCode();
            ERRLOG("Curl ok,HttpStatusCode: %d, Http response error. Error is %s", rsp->GetHttpStatusCode(),
                errorDes.c_str());
            return rsp->GetHttpStatusCode(); // return http status code
        // 2.curl error,need directly retry
        } else {
            errorDes = rsp->GetErrString();
            errorCode = rsp->GetErrCode();
            ERRLOG(" Curl error. errorCode: %d errorDes: %s", errorCode, errorDes.c_str());
        }
        return Module::FAILED;
    // 3. curl success, http response success
    } else {
        errorDes = rsp->GetErrString();
        errorCode = rsp->GetErrCode();
        DBGLOG("curl success and http success");
        return Module::SUCCESS;
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::SendRequestOnce(Module::HttpRequest req, Json::Value &data, std::string &errorDes, int &errorCode)
{
    int iRet = Module::FAILED;
    Module::HttpRequest request = req;
    std::shared_ptr<StorageSession> sessionPtr = std::make_shared<StorageSession>();
    std::shared_ptr<Module::IHttpResponse> rsp;
    for (auto &ip : m_deviceInfo.m_ipList) {
        CheckIpNotEmpty(ip);
        INFOLOG("ip: %s, user: %s, port: %s", ip.c_str(), m_deviceInfo.m_userName.c_str(), m_deviceInfo.m_port.c_str());
        if (m_useCache && m_sessionCache != nullptr) {
            sessionPtr = m_sessionCache->GetSession(ip, m_deviceInfo.m_userName, m_deviceInfo.m_port);
            if (sessionPtr != nullptr) {
                INFOLOG("Get session! ip: %s", ip.c_str());
            }
        } else {
            sessionPtr = m_sessionPtr;
        }
        if (sessionPtr == nullptr || sessionPtr->cookie.empty()) {
            ERRLOG("Invalid session");
            continue;
        }
        request.url = m_curlHttp + ip + ":" + m_deviceInfo.m_port + "/deviceManager/rest/" +
                    sessionPtr->deviceId + "/" + req.url;
        request.isVerify = m_deviceInfo.m_enableCert ? Module::CACertVerification::VCENTER_VERIFY
            : Module::CACertVerification::DO_NOT_VERIFY;
        request.cert = m_deviceInfo.m_cert;
        request.revocationList = m_deviceInfo.m_revocationList;
        (void)request.heads.insert(std::make_pair(std::string("Cookie"), sessionPtr->cookie));
        (void)request.heads.insert(std::make_pair(std::string("iBaseToken"), sessionPtr->token));
        iRet = SendHttpReq(rsp, request, errorDes, errorCode);
        if (iRet != Module::SUCCESS) {
            if (!m_isRetry) {
                return iRet;
            }
            // get when curl send success,http response error for httpstatuscodeforRetry
            continue;
        }
        iRet = ResponseSuccessHandle(req, rsp, data, errorDes, errorCode);
        return iRet;
    }
    return iRet;
}

int32_t ApiOperator::ResponseSuccessHandle(Module::HttpRequest req, std::shared_ptr<Module::IHttpResponse>& rsp,
    Json::Value &data, std::string &errorDes, int &errorCode)
{
    int32_t iRet = ParseResponse(rsp->GetBody(), data, errorDes, errorCode);
    if (errorCode == ApiErrorCode::UNAUTH ||
        errorCode == ApiErrorCode::NOUSERPERMISSION ||
        errorCode == ApiErrorCode::AUTHIPINCONSISTENCY ||
        errorCode == ApiErrorCode::RETURN_OM_AUTH_CONNECT_FAILED) {
        WARNLOG("Session Dorado Failed! ErrorCode: %d, des: %s", errorCode, errorDes.c_str());
        std::string des;
        StorageSessionInfo sessionInfo = Login(des);
        if (sessionInfo.deviceId.empty() || sessionInfo.token.empty() ||
            sessionInfo.cookie.empty() || sessionInfo.ip.empty()) {
            ERRLOG("Login Dorado Failed! deviceId: %s", sessionInfo.deviceId.c_str());
            return Module::FAILED;
        }
        // Refresh session
        if (!RefreshSession(sessionInfo)) {
            ERRLOG("Refresh session failed! session ip: %s", sessionInfo.ip.c_str());
            return Module::FAILED;
        }
        Module::HttpRequest request = req;
        request.url = m_curlHttp + sessionInfo.ip + ":" + m_deviceInfo.m_port + "/deviceManager/rest/" +
                    sessionInfo.deviceId + "/" + req.url;
        (void)request.heads.insert(std::make_pair(std::string("Cookie"), sessionInfo.cookie));
        (void)request.heads.insert(std::make_pair(std::string("iBaseToken"), sessionInfo.token));
        iRet = SendHttpReq(rsp, request, errorDes, errorCode);
        if (iRet != Module::SUCCESS) {
            ERRLOG("SendHttpReq failed! session ip: %s", sessionInfo.ip.c_str());
            return Module::FAILED;
        }
        iRet = ParseResponse(rsp->GetBody(), data, errorDes, errorCode);
    }
    return iRet;
}

bool ApiOperator::RefreshSession(const StorageSessionInfo &sessionInfo)
{
    if (!m_useCache) {
        WARNLOG("Invalid session, refresh! Ip: %s", sessionInfo.ip.c_str());
        if (m_sessionPtr == nullptr) {
        m_sessionPtr = std::make_shared<StorageSession>(sessionInfo.token, sessionInfo.deviceId,
            sessionInfo.cookie);
        }
        m_sessionPtr->token = sessionInfo.token;
        m_sessionPtr->cookie = sessionInfo.cookie;
        m_sessionPtr->deviceId = sessionInfo.deviceId;
    } else {
        if (m_sessionCache == nullptr) {
            ERRLOG("SessionCache pointer is null! Ip:%s", sessionInfo.ip.c_str());
            return false;
        }
        m_sessionCache->DeleteSession(sessionInfo.ip, m_deviceInfo.m_userName, m_deviceInfo.m_port, [
            this](StorageSessionInfo sesInfo) -> int32_t { return Logout(sesInfo); });
        m_sessionCache->RefreshSession(sessionInfo, m_deviceInfo.m_userName, m_deviceInfo.m_port);
    }
    DBGLOG("Refresh Session success, ip:%s", sessionInfo.ip.c_str());
    return true;
}


void ApiOperator::DelayTimeSendRequest()
{
    auto now = std::chrono::steady_clock::now();
    while ((double(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() -
        now).count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den) <
        m_retryIntervalTime) {
        INFOLOG("Waiting for storage device ... ");
        sleep(1);
    }
    return;
}

void ApiOperator::DeleteDeviceSession()
{
    for (auto& ip : m_deviceInfo.m_ipList) {
        m_sessionCache->DeleteSession(ip, m_deviceInfo.m_userName, m_deviceInfo.m_port,
            [this](StorageSessionInfo sesInfo) -> int32_t {return Logout(sesInfo);});
    }
}

void ApiOperator::CreateDeviceSession(std::string &errorDes)
{
    for (auto &ip : m_deviceInfo.m_ipList) {
        if (!m_sessionCache->CreateSession(ip, m_deviceInfo.m_userName, errorDes, m_deviceInfo.m_port,
            [this](std::string &errorStr, const std::string &ip) ->
            StorageSessionInfo {return LoginForIp(errorStr, ip);})) {
            ERRLOG("Create Session failed! IP: %s", ip.c_str());
        }
    }
}

void ApiOperator::LoginAndGetSessionInfo(std::string &errorDes)
{
    if (!m_useCache || m_sessionCache == nullptr) {
        INFOLOG("Not using session cache!");
        StorageSessionInfo sessionInfo = Login(errorDes);
        if (sessionInfo.token.empty() || sessionInfo.cookie.empty() || sessionInfo.deviceId.empty()) {
            ERRLOG("Session is null!");
            m_sessionPtr = nullptr;
        } else {
            m_sessionPtr = std::make_shared<StorageSession>(sessionInfo.token, sessionInfo.deviceId,
                                                            sessionInfo.cookie);
        }
    } else if (!m_isRetry || !CheckStorageSession()) {
        INFOLOG("Create new session cache!");
        CreateDeviceSession(errorDes);
        if (m_sessionPtr == nullptr) {
            m_sessionPtr = std::make_shared<StorageSession>(); // 只是防止判空，意义不大
        }
    }
    return;
}

bool ApiOperator::CheckStorageSession()
{
    DBGLOG("Start check storageSession.");
    if (m_sessionCache == nullptr) {
        ERRLOG("SessionCache is nullptr!");
        return false;
    }
    for (auto &ip : m_deviceInfo.m_ipList) {
        if (!m_sessionCache->CheckSession(ip, m_deviceInfo.m_userName, m_deviceInfo.m_port)) {
            INFOLOG("Check session failed! Ip:%s", ip.c_str());
            return false;
        }
        INFOLOG("Check session success! Ip:%s", ip.c_str());
    }
    if (m_sessionPtr == nullptr) {
        m_sessionPtr = std::make_shared<StorageSession>();
    }
    return true;
}

int32_t ApiOperator::SendRequest(Module::HttpRequest &req, Json::Value &data, std::string &errorDes,
                                 int &errorCode, bool lockSession)
{
    int32_t retryNum = 0;
    while (retryNum < m_retryTimes) {
        INFOLOG("send request for %d time to %s", (retryNum + 1), WIPE_SENSITIVE(req.url).c_str());
        int32_t ret = Module::SUCCESS;
        if (m_useCache) {
            std::lock_guard<std::mutex> guard(m_mutexSession);
            std::string errorIp;
            LoginAndGetSessionInfo(errorIp);
            if (!m_isRetry && !errorIp.empty() && errorIp != "0") {
                ERRLOG("Ip error: %s!", errorIp.c_str());
                errorDes = errorIp;
                break;
            }
            if (!m_useCache && m_sessionPtr == nullptr) {
                ERRLOG("Invalid session");
                break;
            }
        }
        if (lockSession && m_sessionPtr != nullptr) {
            std::lock_guard<std::mutex> lock(m_sessionPtr->sessionMutex);
            ret = SendRequestOnce(req, data, errorDes, errorCode);
        } else {
            ret = SendRequestOnce(req, data, errorDes, errorCode);
        }
        if (ret == Module::SUCCESS) {
            INFOLOG("send requests success ");
            return Module::SUCCESS;
        }
        // 1.when curl success and ret not Module::FAILED, ret is httpStatusCode,
        // so judge whether ret is in httpRspStatusCodeForRetry for retry.
        // 2.when when curl success and ret is Module::FAILED,
        // ResposeNeedRetry, not judge http retry code, directly retry.
        // 3.when errorCode not 0, mean curl failed, directly retry.
        if (errorCode == 0 && !ResposeNeedRetry(ret) &&
            std::find(m_httpRspStatusCodeForRetry.begin(), m_httpRspStatusCodeForRetry.end(), ret)
            == m_httpRspStatusCodeForRetry.end()) {
            INFOLOG("not retry send msg for httpstatuscode:%d", ret);
            break;
        }
        if (CertErrorCode.find(errorCode) != CertErrorCode.end()) {
            break;
        }
        DelayTimeSendRequest();
        ++retryNum;
    }
    ERRLOG("send request failed.");
    return Module::FAILED;
}

bool ApiOperator::IsNeedRetryErrorCode(const int32_t &errorCode)
{
    int32_t size = sizeof(RetryErrorCode::NO_NEED_RETRY_ERROR_CODE) / sizeof(int32_t);
    for (int32_t i = 0; i < size; ++i) {
        if (errorCode == RetryErrorCode::NO_NEED_RETRY_ERROR_CODE[i]) {
            return false;
        }
    }
    return true;
}

void ApiOperator::InitHttpStatusCodeForRetry()
{
    Module::ConfigReader::getIntValueVector(Module::MS_CFG_MICROSERVICE_SECTION, Module::MS_CFG_HTTP_RETRY_ERROR_CODES,
        ",", m_httpRspStatusCodeForRetry);
}

void ApiOperator::InitHttpConfig()
{
    m_curlTimeOut = std::max((uint64_t)Module::ConfigReader::getInt("DoradoCurlConf", "CurlTimeOut"), m_curlTimeOut);
    m_retryIntervalTime = std::max(Module::ConfigReader::getInt("DoradoCurlConf", "CurlRetryIntervalTime"), m_retryIntervalTime);
    m_retryTimes = std::max(Module::ConfigReader::getInt("DoradoCurlConf", "CurlRetryTime"), m_retryTimes);
    INFOLOG("Curl time out %d, curl retry time %d, retry interval time %d.", m_curlTimeOut, m_retryTimes, m_retryIntervalTime);
}

bool ApiOperator::ResposeNeedRetry(const int32_t ret)
{
    // when errorCode ==0 && ret == Module::FAILED mean storage response need retry
    if (ret == Module::FAILED) {
        return true;
    }
    return false;
}

bool ApiOperator::CheckDosAttack()
{
    std::lock_guard<std::mutex> guard(m_mutexDos);
    int iDosReqInterval = Module::ConfigReader::getInt("BackupNode", "DosRequestInterval");
    int iDosReqCount = Module::ConfigReader::getInt("BackupNode", "DosRequestCount");
    uint64_t tNow = static_cast<uint64_t>(time(nullptr));
    if (iDosReqInterval == 0) {
        iDosReqInterval = DOS_REQ_INTERVAL;
    }
    if (iDosReqCount == 0) {
        iDosReqCount = DOS_REQ_COUNT;
    }
    /* If request is too much during the interval, should delay to send the next request */
    if ((tNow - m_lastReqTimeStamp < static_cast<uint64_t>(iDosReqInterval)) &&
        (m_reqCounts >= static_cast<uint64_t>(iDosReqCount))) {
        WARNLOG("The rest request exceed %d per %d seconds", iDosReqCount, iDosReqInterval);
        return true;
    } else {
        /* Reset our request count as necessary */
        if (tNow - m_lastReqTimeStamp >= static_cast<uint64_t>(iDosReqInterval)) {
            m_reqCounts = 0;
        }
    }
    m_lastReqTimeStamp = tNow;
    ++m_reqCounts;
    DBGLOG("The rest request counts is %lld", m_reqCounts);
    return false;
}

int32_t ApiOperator::GetSystemInfo(StorageSysInfo &storageSysInfo, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("system/");
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty()) {
        if (!data.isMember("ID") || !data.isMember("wwn") || !data.isMember("PRODUCTMODE")) {
            ERRLOG("The response param error, id error.");
            return Module::FAILED;
        }
        storageSysInfo.m_id = data["ID"].asString();
        storageSysInfo.m_wwn = data["wwn"].asString();
        storageSysInfo.m_productMode = data["PRODUCTMODE"].asString();
        return Module::SUCCESS;
    }
    ERRLOG("Get system info failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetUserRoleAndLevel(const std::string& userName, UserRoleLevel &userInfo, std::string &errorDes,
    const std::string& productMode)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "user/" + userName;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty()) {
        std::string level = "";
        std::string roleID = "";
        ApiOperatorManager::GetInstance()->GetLevelAndRoleID(productMode, level, roleID);
        if (data.isMember(level)) {
            userInfo.m_level = data[level].asString();
        }
        if (data.isMember(roleID)) {
            userInfo.m_roleID = data[roleID].asString();
        }
        INFOLOG("Get user info success. %s: %s, %s: %s", level.c_str(), userInfo.m_level.c_str(),
            roleID.c_str(), userInfo.m_roleID.c_str());
        return Module::SUCCESS;
    }
    ERRLOG("Get user info failed, error: %d, des: %s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetServerStatus(std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("server/status");
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        return Module::SUCCESS;
    }
    ERRLOG("Get system info failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetLunByID(const std::string &lunId, LunMO &lunMo, std::string &errorDes)
{
    DBGLOG("Start get lun info, lun id:%s", lunId.c_str());
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "lun/" + lunId;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty()) {
        if (!data.isMember("ID") || data["ID"].asString() != lunId) {
            ERRLOG("The response param error, id error.");
            return Module::FAILED;
        }
        lunMo.m_id = data["ID"].asString();
        lunMo.m_name = data["NAME"].asString();
        lunMo.m_parentType = MO_STORAGEPOOL;
        lunMo.m_parentID = data["PARENTID"].asString();
        lunMo.m_parentName = data["PARENTNAME"].asString();
        lunMo.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data["HEALTHSTATUS"].asString());
        lunMo.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data["RUNNINGSTATUS"].asString());
        lunMo.m_description = data["DESCRIPTION"].asString();
        lunMo.m_allocType = (LUN_ALLOC_TYPE_E)std::stoul(data["ALLOCTYPE"].asString());
        uint64_t sectorSize = std::stoull(data["SECTORSIZE"].asString());
        lunMo.m_capacity = std::stoull(data["CAPACITY"].asString()) * sectorSize;
        lunMo.m_initialAllocCapacity = std::stoull(data["ALLOCCAPACITY"].asString()) * sectorSize;
        lunMo.m_allocCapacity = std::stoull(data["ALLOCCAPACITY"].asString());
        if (lunMo.m_allocType == SAN_THIN && data.isMember("THINCAPACITYUSAGE")) {
            lunMo.m_thinCapacityUsage = std::stoull(data["THINCAPACITYUSAGE"].asString());
        }
        lunMo.m_exposedToInitiator = (data["EXPOSEDTOINITIATOR"].asString() == "true") ? true : false;
        lunMo.m_wwn = data["WWN"].asString();
        lunMo.m_owningController = data["OWNINGCONTROLLER"].asString();
        lunMo.m_workingController = data["WORKINGCONTROLLER"].asString();
        lunMo.m_isAddToLunGroup = (data["ISADD2LUNGROUP"].asString() == "true") ? true : false;
        DBGLOG("Lun info: %s %s %lld %lld %lld", lunMo.m_name.c_str(), lunMo.m_wwn.c_str(),
            lunMo.m_capacity, lunMo.m_initialAllocCapacity, lunMo.m_allocCapacity);
        return Module::SUCCESS;
    }
    ERRLOG("Get lun by id failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetLunByName(const std::string &lunName, LunMO &lunMo, std::string &errorDes)
{
    std::string newLunName = lunName;
    if (newLunName.length() > MAX_LENGTH) {
        newLunName = newLunName.substr(0, MAX_LENGTH);
    }
    DBGLOG("Start get lun info name:%s", newLunName.c_str());
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "lun?filter=NAME::" + newLunName;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty() && data.isArray()) {
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i]["NAME"].asString() != newLunName || data[i]["DESCRIPTION"].asString() != lunName) {
                continue;
            }
            lunMo.m_id = data[i]["ID"].asString();
            lunMo.m_name = data[i]["NAME"].asString();
            lunMo.m_parentType = MO_STORAGEPOOL;
            lunMo.m_parentID = data[i]["PARENTID"].asString();
            lunMo.m_parentName = data[i]["PARENTNAME"].asString();
            lunMo.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data[i]["HEALTHSTATUS"].asString());
            lunMo.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data[i]["RUNNINGSTATUS"].asString());
            lunMo.m_description = data[i]["DESCRIPTION"].asString();
            lunMo.m_allocType = (LUN_ALLOC_TYPE_E)std::stoul(data[i]["ALLOCTYPE"].asString());
            uint64_t sectorSize = std::stoull(data[i]["SECTORSIZE"].asString());
            lunMo.m_capacity = std::stoull(data[i]["CAPACITY"].asString()) * sectorSize;
            lunMo.m_initialAllocCapacity = std::stoull(data[i]["ALLOCCAPACITY"].asString()) * sectorSize;
            lunMo.m_allocCapacity = std::stoull(data[i]["ALLOCCAPACITY"].asString());
            if (lunMo.m_allocType == SAN_THIN && data[i].isMember("THINCAPACITYUSAGE")) {
                lunMo.m_thinCapacityUsage = std::stoull(data[i]["THINCAPACITYUSAGE"].asString());
            }
            lunMo.m_exposedToInitiator = (data[i]["EXPOSEDTOINITIATOR"].asString() == "true") ? true : false;
            lunMo.m_wwn = data[i]["WWN"].asString();
            lunMo.m_owningController = data[i]["OWNINGCONTROLLER"].asString();
            lunMo.m_workingController = data[i]["WORKINGCONTROLLER"].asString();
            lunMo.m_isAddToLunGroup = (data[i]["ISADD2LUNGROUP"].asString() == "true") ? true : false;
            DBGLOG("Lun info: %s %s %lld %lld %lld", lunMo.m_name.c_str(), lunMo.m_wwn.c_str(),
                lunMo.m_capacity, lunMo.m_initialAllocCapacity, lunMo.m_allocCapacity);
            return Module::SUCCESS;
        }
        ERRLOG("Get lun by name failed, not find.");
        return Module::FAILED;
    }
    ERRLOG("Get lun by name failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetLunChunkSize(const std::string &lunID, uint64_t &chunkSize, std::string &errorDes)
{
    DBGLOG("Start get lun chunk size, lun id: %s", lunID.c_str());
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "lun_bitmap/get_bitmap_chks?LUNID=" + lunID;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        if (!data.isMember("BITMAPCHUNKSIZE")) {
            ERRLOG("The response param error, bit map chunk size not exist.");
            return Module::FAILED;
        }
        chunkSize = std::stol(data["BITMAPCHUNKSIZE"].asString());
        return Module::SUCCESS;
    }
    ERRLOG("Get lun chunk size failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetLunAllocBitmap(const std::string &objectId, DiffBitmap &diffBitmap, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "lun_bitmap/get_alloc?BITMAPCHUNKSIZE=" + std::to_string(diffBitmap.m_chunkSize) + "&LUNID=" + objectId +
              "&SEGMENTLENGTHBYTES=" + std::to_string(diffBitmap.m_size) +
              "&SEGMENTSTARTOFFSETBYTES=" + std::to_string(diffBitmap.m_offset);
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        if (!data.isMember("CHUNKBITMAP")) {
            ERRLOG("The response param error, chunkbitmap is empty.");
            return Module::FAILED;
        }
        diffBitmap.m_bitmap = data["CHUNKBITMAP"].asString();
        return Module::SUCCESS;
    }
    ERRLOG("Get lun alloc bitmap failed, error: %d, des: %s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetLunUnsharedBitmap(const std::string &objectId, const std::string &parentObjectId,
    DiffBitmap &diffBitmap, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "lun_bitmap/get_unshared?BASELUNID=" + parentObjectId + "&LUNID=" + objectId +
              "&BITMAPCHUNKSIZE=" + std::to_string(diffBitmap.m_chunkSize) +
              "&SEGMENTLENGTHBYTES=" + std::to_string(diffBitmap.m_size) +
              "&SEGMENTSTARTOFFSETBYTES=" + std::to_string(diffBitmap.m_offset);
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        if (!data.isMember("CHUNKBITMAP")) {
            ERRLOG("The response param error, chunkbitmap is empty.");
            return Module::FAILED;
        }
        diffBitmap.m_bitmap = data["CHUNKBITMAP"].asString();
        return Module::SUCCESS;
    }
    ERRLOG("Get lun unshared bitmap failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetSnapshotByName(const std::string &snapshotName, SnapshotMO &snapshotMO, std::string &errorDes)
{
    std::string newSnapshotName = snapshotName;
    if (newSnapshotName.length() > MAX_LENGTH) {
        newSnapshotName = newSnapshotName.substr(0, MAX_LENGTH);
    }
    INFOLOG("Start query snapshot %s", newSnapshotName.c_str());
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "SNAPSHOT?filter=NAME::" + newSnapshotName;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && data.size() > 0) {
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (!data[i].isMember("NAME") || data[i]["NAME"].asString() != newSnapshotName ||
                !data[i].isMember("DESCRIPTION") || data[i]["DESCRIPTION"].asString() != snapshotName) {
                continue;
            }
            snapshotMO.m_id = data[i]["ID"].asString();
            snapshotMO.m_name = data[i]["NAME"].asString();
            snapshotMO.m_parentType = (MO_TYPE)data[i]["PARENTTYPE"].asInt();
            snapshotMO.m_parentID = data[i]["PARENTID"].asString();
            snapshotMO.m_parentName = data[i]["PARENTNAME"].asString();
            snapshotMO.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data[i]["HEALTHSTATUS"].asString());
            snapshotMO.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data[i]["RUNNINGSTATUS"].asString());
            snapshotMO.m_description = data[i]["DESCRIPTION"].asString();
            snapshotMO.m_userCapacity = std::stoull(data[i]["USERCAPACITY"].asString()) * CAPACITY_COEFFICIENT;
            snapshotMO.m_consumedCapacity = std::stoull(data[i]["CONSUMEDCAPACITY"].asString());
            snapshotMO.m_wwn = data[i]["WWN"].asString();
            return Module::SUCCESS;
        }
    }
    ERRLOG("Query snapshot failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetSnapShotByID(const std::string &snapShotId, SnapshotMO &snapshotMO, std::string &errorDes)
{
    DBGLOG("Start get snapshot info: %s", snapShotId.c_str());
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "snapshot/" + snapShotId;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        INFOLOG("Start to get snap data");
        snapshotMO.m_id = data["ID"].asString();
        snapshotMO.m_name = data["NAME"].asString();
        snapshotMO.m_parentType = (MO_TYPE)data["PARENTTYPE"].asInt();
        snapshotMO.m_parentID = data["PARENTID"].asString();
        snapshotMO.m_parentName = data["PARENTNAME"].asString();
        snapshotMO.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data["HEALTHSTATUS"].asString());
        snapshotMO.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data["RUNNINGSTATUS"].asString());
        snapshotMO.m_description = data["DESCRIPTION"].asString();
        snapshotMO.m_userCapacity = std::stoull(data["USERCAPACITY"].asString()) * CAPACITY_COEFFICIENT;
        snapshotMO.m_consumedCapacity = std::stoull(data["CONSUMEDCAPACITY"].asString());
        snapshotMO.m_wwn = data["WWN"].asString();
        DBGLOG("SnapShot info: %s %s %s %lld %lld", snapshotMO.m_name.c_str(), snapshotMO.m_wwn.c_str(),
            snapshotMO.m_parentID.c_str(), snapshotMO.m_userCapacity, snapshotMO.m_consumedCapacity);
        return Module::SUCCESS;
    }
    ERRLOG("Query snapshot failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::CreateHost(const std::string &hostName, const std::string &hostIp, HostMO &hostMO,
    std::string &errorDes)
{
    INFOLOG("Start create host %s", hostName.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    Json::FastWriter jsonWriter;
    jsonReq["NAME"] = hostName;
    if (!hostIp.empty()) {
        jsonReq["IP"] = hostIp;
    }
    jsonReq["OPERATIONSYSTEM"] = LINUX_TYPE;
    req.method = "POST";
    req.url = "host";
    req.body = jsonWriter.write(jsonReq);
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        if (!data.isMember("ID")) {
            ERRLOG("The response param error, id is empty.");
            return Module::FAILED;
        }
        hostMO.m_id = data["ID"].asString();
        hostMO.m_name = data["NAME"].asString();
        return Module::SUCCESS;
    }
    if (iRet == Module::SUCCESS && errorCode == ApiErrorCode::HOSTEXIST) {
        return GetHostByName(hostName, hostMO, errorDes);
    }
    ERRLOG("Create host failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetHostByID(const std::string &hostID, HostMO &hostMO, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("host/" + hostID);
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty()) {
        if (!data.isMember("ID") || data["ID"].asString() != hostID) {
            ERRLOG("The response param error, id error.");
            return Module::FAILED;
        }
        hostMO.m_id = data["ID"].asString();
        hostMO.m_name = data["NAME"].asString();
        hostMO.m_parentType = (MO_TYPE)data["PARENTTYPE"].asUInt();
        hostMO.m_location = data["LOCATION"].asString();
        hostMO.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data["HEALTHSTATUS"].asString());
        hostMO.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data["RUNNINGSTATUS"].asString());
        hostMO.m_description = data["DESCRIPTION"].asString();
        hostMO.m_operationSystem = (OS_TYPE_E)std::stoul(data["OPERATIONSYSTEM"].asString());
        hostMO.m_isAddToHostGroup = (data["ISADD2HOSTGROUP"].asString() == "true") ? true : false;
        hostMO.m_ip = data["IP"].asString();
        hostMO.m_parentId = data["PARENTID"].asString();
        return Module::SUCCESS;
    }
    ERRLOG("Get host by id failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetHostByName(const std::string &hostName, HostMO &hostMO, std::string &errorDes)
{
    INFOLOG("Start query host:%s", hostName.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    req.method = "GET";
    req.url = "host?filter=NAME::" + hostName;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && data.size() > 0) {
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i]["NAME"].asString() != hostName) {
                continue;
            }
            hostMO.m_id = data[i]["ID"].asString();
            hostMO.m_name = data[i]["NAME"].asString();
            hostMO.m_parentType = (MO_TYPE)data[i]["PARENTTYPE"].asUInt();
            hostMO.m_location = data[i]["LOCATION"].asString();
            hostMO.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data[i]["HEALTHSTATUS"].asString());
            hostMO.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data[i]["RUNNINGSTATUS"].asString());
            hostMO.m_description = data[i]["DESCRIPTION"].asString();
            hostMO.m_operationSystem = (OS_TYPE_E)std::stoul(data[i]["OPERATIONSYSTEM"].asString());
            hostMO.m_isAddToHostGroup = (data[i]["ISADD2HOSTGROUP"].asString() == "true") ? true : false;
            hostMO.m_ip = data[i]["IP"].asString();
            hostMO.m_parentId = data[i]["PARENTID"].asString();
            return Module::SUCCESS;
        }
    }
    ERRLOG("Get host by name failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetIscsiLogicPortIP(std::vector<std::string> &storageIps, std::string &errorDes)
{
    INFOLOG("Start get iscsi logic port list.");
    Module::HttpRequest req;
    Json::Value jsonReq;
    req.method = "GET";
    req.url = "lif";
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet != Module::SUCCESS || errorCode != Module::SUCCESS) {
        ERRLOG("Query iscsi lif failed, error:%d, des:%s", errorCode, errorDes.c_str());
        return (errorCode == 0) ? Module::FAILED : errorCode;
    }
    for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
        std::string storageIP;
        std::string storageIPv4;
        std::string storageIPv6;
        if (data[i]["SUPPORTPROTOCOL"].asString() != SUPPORTPROTOCOL_ISCSI ||
            data[i]["ROLE"].asString() != PORT_ROLE_REPLICATION &&
            data[i]["RUNNINGSTATUS"].asString() != RUNNINGSTATUS_LINKUP) {
            continue;
        }
        if (data[i].isMember("IPV4ADDR")) {
            storageIPv4 = data[i]["IPV4ADDR"].asString();
        }
        if (data[i].isMember("IPV6ADDR")) {
            storageIPv6 = data[i]["IPV6ADDR"].asString();
        }
        if (!GetIpByNetworkType(storageIPv4, storageIPv6, storageIP)) {
            ERRLOG("Get ip by network type failed.");
            return Module::FAILED;
        }
        if (storageIP.length() > 0) {
            storageIps.push_back(storageIP);
            DBGLOG("Iscsi lif ip:%s", storageIP.c_str());
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::GetEthPortIP(std::vector<std::string> &storageIps, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "eth_port?LOGICTYPE=0";
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet != Module::SUCCESS || errorCode != Module::SUCCESS) {
        ERRLOG("Get eth port failed.");
        return (errorCode == 0) ? Module::FAILED : errorCode;
    }
    std::string storageIP;
    for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
        if (ParseEthPortIPJson(data, i, storageIP) != Module::SUCCESS) {
            ERRLOG("Parse json ip failed");
            return Module::FAILED;
        }
        if (storageIP.length() > 0) {
            storageIps.push_back(storageIP);
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::GetBondPortIP(std::vector<std::string> &storageIps, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "bond_port";
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet != Module::SUCCESS || errorCode != Module::SUCCESS) {
        ERRLOG("Get bond port failed.");
        return (errorCode == 0) ? Module::FAILED : errorCode;
    }
    std::string storageIP;
    for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
        std::string storageIP;
        std::string storageIPv4;
        std::string storageIPv6;
        if (data[i].isMember("IPV4ADDR")) {
            storageIPv4 = data[i]["IPV4ADDR"].asString();
        }
        if (data[i].isMember("IPV6ADDR")) {
            storageIPv6 = data[i]["IPV6ADDR"].asString();
        }
        if (!GetIpByNetworkType(storageIPv4, storageIPv6, storageIP)) {
            ERRLOG("Get ip by network type failed.");
            return Module::FAILED;
        }
        if (storageIP.length() > 0) {
            storageIps.push_back(storageIP);
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::GetTargetIP(std::vector<std::string> &storageIps, std::string &errorDes)
{
    if (GetEthPortIP(storageIps, errorDes) != Module::SUCCESS) {
        ERRLOG("Get storage ips failed");
        return Module::FAILED;
    }
    if (GetBondPortIP(storageIps, errorDes) != Module::SUCCESS) {
        ERRLOG("Get bond ips failed");
        return Module::FAILED;
    }
    if (GetIscsiLogicPortIP(storageIps, errorDes) != Module::SUCCESS) {
        WARNLOG("Query iscsi lif failed!");
    }
    sort(storageIps.begin(), storageIps.end());
    auto it = unique(storageIps.begin(), storageIps.end());
    storageIps.erase(it, storageIps.end());
    return Module::SUCCESS;
}

int32_t ApiOperator::ParseEthPortIPJson(Json::Value data, uint32_t i, std::string &storageIP)
{
    std::string storageIPv4 = "";
    std::string storageIPv6 = "";
    try {
        if (data[i].isMember("IPV4ADDR")) {
            storageIPv4 = data[i]["IPV4ADDR"].asString();
            DBGLOG("ipv4 addr is %s", storageIPv4.c_str());
        }
        if (data[i].isMember("IPV6ADDR")) {
            storageIPv6 = data[i]["IPV6ADDR"].asString();
            DBGLOG("ipv6 addr is %s", storageIPv6.c_str());
        }
        if (!(data[i].isMember("IPV4ADDR")) && !(data[i].isMember("IPV6ADDR"))) {
            ERRLOG("ipv4 addr or ipv6 addr not found");
            return Module::FAILED;
        }
    } catch (std::exception &e) {
        ERRLOG("Throw a exception.%s", WIPE_SENSITIVE(e.what()));
        return Module::FAILED;
    }
    storageIP = "";
    if (!GetIpByNetworkType(storageIPv4, storageIPv6, storageIP)) {
        ERRLOG("Get ip by network type failed.");
        return Module::FAILED;
    }
    DBGLOG("Get storage ip:%s", storageIP.c_str());
    return Module::SUCCESS;
}

bool ApiOperator::GetIpByNetworkType(const std::string &storageIPv4, const std::string &storageIPv6,
    std::string &storageIP)
{
    if (storageIPv4.empty() && storageIPv6.empty()) {
        return true;
    }
    std::string type = Module::ConfigReader::getString(Module::MS_CFG_GENERAL_SECTION, "NetworkType");
    DBGLOG("Network type:%s, storage ipv4:%s, storage ipv6:%s", type.c_str(), storageIPv4.c_str(), storageIPv6.c_str());
    if (type.empty()) {
        ERRLOG("Network type is empty, should be ipv4 or ipv6.");
        return false;
    }
    if (type == "ipv4" && !storageIPv4.empty()) {
        storageIP = storageIPv4;
        return true;
    }
    if (type == "ipv6" && !storageIPv6.empty()) {
        storageIP = storageIPv6;
        return true;
    }
    WARNLOG("Network type:%s, storage ipv4:%s, storage ipv6:%s", type.c_str(), storageIPv4.c_str(),
        storageIPv6.c_str());
    return true;
}

int32_t ApiOperator::CreateIscsiInitiator(const std::string &id, std::string &errorDes)
{
    INFOLOG("Start create iscsi port.");
    Module::HttpRequest req;
    Json::Value jsonReq;
    Json::FastWriter jsonWriter;
    jsonReq["ID"] = id;
    req.method = "POST";
    req.url = "iscsi_initiator";
    req.body = jsonWriter.write(jsonReq);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::RETURN_OBJ_ID_EXIST)) {
        return Module::SUCCESS;
    }
    ERRLOG("Create iscsi port failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetIscsiInitiatorByID(const std::string &id, IscsiInitiatorMO &oRtnIscsiInitiatorMO,
    std::string &errorDes)
{
    DBGLOG("Start query iscsi initiator.");
    Module::HttpRequest req;
    Json::Value jsonReq;
    req.method = "GET";
    req.url = "iscsi_initiator/" + id;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        if (ParseIscsiInfo(id, oRtnIscsiInitiatorMO, data) != Module::SUCCESS) {
            return Module::FAILED;
        }
        return Module::SUCCESS;
    }
    ERRLOG("Query iscsi failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::ParseIscsiInfo(const std::string &id, IscsiInitiatorMO &oRtnIscsiInitiatorMO, Json::Value &data)
{
    if (!data.isMember("ID") || data["ID"].asString() != id) {
        ERRLOG("Parse iscsi failed, id not exist.");
        return Module::FAILED;
    }
    oRtnIscsiInitiatorMO.m_id = data["ID"].asString();
    if (data.isMember("NAME")) {
        oRtnIscsiInitiatorMO.m_name = data["NAME"].asString();
    }
    if (data.isMember("PARENTID")) {
        oRtnIscsiInitiatorMO.m_parentID = data["PARENTID"].asString();
    }
    if (data.isMember("PARENTNAME")) {
        oRtnIscsiInitiatorMO.m_parentName = data["PARENTNAME"].asString();
    }
    oRtnIscsiInitiatorMO.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data["HEALTHSTATUS"].asString());
    oRtnIscsiInitiatorMO.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data["RUNNINGSTATUS"].asString());
    oRtnIscsiInitiatorMO.m_isFree = (data["ISFREE"].asString() == "true") ? true : false;
    oRtnIscsiInitiatorMO.m_useChap = (data["USECHAP"].asString() == "true") ? true : false;
    if (data.isMember("CHAPNAME")) {
        oRtnIscsiInitiatorMO.m_chapName = data["CHAPNAME"].asString();
    }
    if (data.isMember("CHAPNAMEINITATORVERTARGET")) {
        oRtnIscsiInitiatorMO.m_chapNameTarget = data["CHAPNAMEINITATORVERTARGET"].asString();
    }
    if (data.isMember("NORMALVERMODE")) {
        oRtnIscsiInitiatorMO.m_normalVerMode = data["NORMALVERMODE"].asString();
    }
    if (data.isMember("DISCOVERYVERMODE")) {
        oRtnIscsiInitiatorMO.m_discoveryVerMode = data["DISCOVERYVERMODE"].asString();
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::GetAllIscsiInitiatorByHostID(const std::string &hostID, std::vector<std::string> &iscsiIds,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "iscsi_initiator?PARENTID=" + hostID;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet != Module::SUCCESS || errorCode != Module::SUCCESS) {
        ERRLOG("Query iscsi failed, error:%d, des:%s", errorCode, errorDes.c_str());
        return (errorCode == 0) ? Module::FAILED : errorCode;
    }
    std::string initiator;
    for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
        try {
            if (data[i].isMember("ID")) {
                initiator = data[i]["ID"].asString();
                iscsiIds.push_back(initiator);
            }
        } catch (std::exception &e) {
            ERRLOG("Throw a exception.%s", WIPE_SENSITIVE(e.what()));
            return Module::FAILED;
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::AddIscsiInitiatorToHost(const std::string& iscsiID, const std::string& hostID,
    std::string &errorDes)
{
    INFOLOG("Start bind iscsi port to host:%s", hostID.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    Json::FastWriter jsonWriter;
    jsonReq["PARENTTYPE"] = HOST_TYPE;
    jsonReq["PARENTID"] = hostID;
    req.method = "PUT";
    req.url = "iscsi_initiator/" + iscsiID;
    req.body = jsonWriter.write(jsonReq);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        return Module::SUCCESS;
    }
    ERRLOG("Add iscsi to host failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::RemoveIscsiInitiatorFromHost(const std::string &iscsiID, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "PUT";
    req.url = std::string("iscsi_initiator/remove_iscsi_from_host");
    Json::Value jsonBody;
    jsonBody["TYPE"] = "222";
    jsonBody["ID"] = iscsiID;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS) {
        if (errorCode == ApiErrorCode::ISCSI_INITIATOR_NOTIN) {
            INFOLOG("The iscsi initiator want to remove not exist on the host!");
            return Module::SUCCESS;
        }
        ERRLOG("Remove iscsi initiator failed! reason: %s", errorDes.c_str());
        return Module::FAILED;
    }
    ERRLOG("Remove iscsi from host failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::CreateFcInitiator(const std::string &id, FCInitiatorMO &fcMO, std::string &errorDes)
{
    INFOLOG("Start create fc port.");
    Module::HttpRequest req;
    Json::Value jsonReq;
    Json::FastWriter jsonWriter;
    jsonReq["ID"] = id;
    req.method = "POST";
    req.url = "fc_initiator";
    req.body = jsonWriter.write(jsonReq);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        fcMO.m_id = data["ID"].asString();
        fcMO.m_name = data["NAME"].asString();
        fcMO.m_parentID = data["PARENTID"].asString();
        fcMO.m_parentName = data["PARENTNAME"].asString();
        fcMO.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data["RUNNINGSTATUS"].asString());
        fcMO.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data["HEALTHSTATUS"].asString());
        fcMO.m_isFree = (data["ISFREE"].asString() == "true") ? true : false;
        return Module::SUCCESS;
    }
    ERRLOG("Create fc port failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetFcInitiatorByID(const std::string &id, FCInitiatorMO &fcMO, std::string &errorDes)
{
    INFOLOG("Start query FC port.");
    Module::HttpRequest req;
    Json::Value jsonReq;
    req.method = "GET";
    req.url = "fc_initiator/" + id;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        fcMO.m_id = data["ID"].asString();
        fcMO.m_name = data["NAME"].asString();
        fcMO.m_parentID = data["PARENTID"].asString();
        fcMO.m_parentName = data["PARENTNAME"].asString();
        fcMO.m_runningStatus = (RUNNING_STATUS_E)std::stoul(data["RUNNINGSTATUS"].asString());
        fcMO.m_healthStatus = (HEALTH_STATUS_E)std::stoul(data["HEALTHSTATUS"].asString());
        fcMO.m_isFree = (data["ISFREE"].asString() == "true") ? true : false;
        return Module::SUCCESS;
    }
    ERRLOG("Query fc port failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetAllFcInitiatorByHostID(const std::string &hostID, std::vector<std::string> &fcIds,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "fc_initiator?PARENTID=" + hostID;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet != Module::SUCCESS || errorCode != Module::SUCCESS) {
        ERRLOG("Query fc failed, error:%d, des:%s", errorCode, errorDes.c_str());
        return (errorCode == 0) ? Module::FAILED : errorCode;
    }
    std::string initiator;
    for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
        try {
            if (data[i].isMember("ID")) {
                initiator = data[i]["ID"].asString();
                fcIds.push_back(initiator);
            }
        } catch (std::exception &e) {
            ERRLOG("Throw a exception.%s", WIPE_SENSITIVE(e.what()));
            return Module::FAILED;
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::AddFcInitiatorToHost(const std::string& fcInitiatorID, const std::string& hostID,
    std::string &errorDes)
{
    INFOLOG("Start bind fc port to host:%s", hostID.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    Json::FastWriter jsonWriter;
    jsonReq["PARENTTYPE"] = HOST_TYPE;
    jsonReq["PARENTID"] = hostID;
    req.method = "PUT";
    req.url = "fc_initiator/" + fcInitiatorID;
    req.body = jsonWriter.write(jsonReq);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        return Module::SUCCESS;
    }
    ERRLOG("Add fc to host failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::RemoveFcInitiatorFromHost(const std::string &fcInitiatorID, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "PUT";
    req.url = std::string("fc_initiator/remove_fc_from_host");
    Json::Value jsonBody;
    jsonBody["TYPE"] = "223";
    jsonBody["ID"] = fcInitiatorID;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS) {
        if (errorCode == ApiErrorCode::FC_INITIATOR_NOTIN) {
            INFOLOG("The fc initiator want to remove not exist on the host!");
            return Module::SUCCESS;
        }
        ERRLOG("Remove fc initiator failed! reason: %s", errorDes.c_str());
        return Module::FAILED;
    }
    ERRLOG("Remove fc from host failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::CreateLunGroup(const std::string &lunGroupName, LunGroupMO &lunGroupMO, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "POST";
    req.url = std::string("lungroup");
    Json::Value jsonBody;
    jsonBody["TYPE"] = MO_LUNGROUP;
    jsonBody["NAME"] = lunGroupName;
    jsonBody["GROUPTYPE"] = GRP_LUNGRP;
    jsonBody["APPTYPE"] = APP_OTHER;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        lunGroupMO.m_id = data["ID"].asString();
        lunGroupMO.m_name = data["NAME"].asString();
        return Module::SUCCESS;
    }
    ERRLOG("Create lun group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::AddObjToLunGroup(const std::string& lunGroupID, const std::string& objId, MO_TYPE objType,
    std::string &errorDes)
{
    INFOLOG("Add obj(id:%s) to lun group(id:%s)", objId.c_str(), lunGroupID.c_str());
    Module::HttpRequest req;
    req.method = "POST";
    req.url = std::string("lungroup/associate");
    Json::Value jsonBody;
    jsonBody["ID"] = lunGroupID;
    jsonBody["ASSOCIATEOBJTYPE"] = std::to_string(objType);
    jsonBody["ASSOCIATEOBJID"] = objId;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        return Module::SUCCESS;
    }
    ERRLOG("Add obj to lun group failed! reason: %s", errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::RemoveObjFromLunGroup(const std::string& lunGroupID, const std::string& objId, MO_TYPE objType,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = std::string("lungroup/associate?ID=" + lunGroupID + "&ASSOCIATEOBJTYPE=" + std::to_string(objType) +
        "&ASSOCIATEOBJID=" + objId);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::OBJECT_NOTEXIST)) {
        return Module::SUCCESS;
    }
    ERRLOG("Remove obj to lun group failed! reason: %s", errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::RemovedObjFromAllLunGroup(const std::string &objId, MO_TYPE objType, std::string &errorDes)
{
    std::vector<std::string> lunGroupIds;
    if (GetLunGroupsByObjId(objId, objType, lunGroupIds, errorDes) != Module::SUCCESS) {
        ERRLOG("Get lun group by snapshot id failed, des:%s", errorDes.c_str());
        return Module::FAILED;
    }
    for (const std::string &lunGroupId : lunGroupIds) {
        RemoveObjFromLunGroup(lunGroupId, objId, objType, errorDes);
    }
    INFOLOG("Remove obj from all lun group success");
    return Module::SUCCESS;
}

int32_t ApiOperator::GetLunGroupsByObjId(const std::string& objId, MO_TYPE objType,
    std::vector<std::string> &lunGroupIds, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("lungroup/associate?ASSOCIATEOBJTYPE=" + std::to_string(objType) + "&ASSOCIATEOBJID=" +
        objId);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet != Module::SUCCESS || errorCode != Module::SUCCESS) {
        ERRLOG("Query lungroup by snapshot id failed, error:%d, des:%s", errorCode, errorDes.c_str());
        return (errorCode == 0) ? Module::FAILED : errorCode;
    }
    std::string lunGroupId;
    for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
        try {
            if (data[i].isMember("ID")) {
                lunGroupId = data[i]["ID"].asString();
                lunGroupIds.push_back(lunGroupId);
            }
        } catch (std::exception &e) {
            ERRLOG("Throw a exception.%s", WIPE_SENSITIVE(e.what()));
            return Module::FAILED;
        }
    }
    INFOLOG("Get lun group by obj success, lun group count:%d", lunGroupIds.size());
    return Module::SUCCESS;
}

int32_t ApiOperator::AssocGetObjIdsByLunGroupID(const std::string &lunGroupId, MO_TYPE objType,
    std::vector<std::string> &objIds, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    if (objType == MO_LUN) {
        req.url = std::string("lun/associate?TYPE=11&ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=" + lunGroupId);
    } else if (objType == MO_SNAPSHOT) {
        req.url = std::string("snapshot/associate?TYPE=27&ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=" + lunGroupId);
    } else {
        ERRLOG("Query all obj id from lun group failed, error obj type:%d", objType);
        return Module::FAILED;
    }
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty() && data.isArray()) {
        std::string objId;
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i].isMember("ID")) {
                objId = data[i]["ID"].asString();
                objIds.push_back(objId);
            }
        }
        return Module::SUCCESS;
    }
    ERRLOG("Query all obj id from lun group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetLunGroupByName(const std::string &lunGroupName, LunGroupMO &lunGroupMO, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "lungroup?filter=NAME::" + lunGroupName;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty() && data.isArray()) {
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i]["NAME"].asString() != lunGroupName) {
                continue;
            }
            lunGroupMO.m_id = data[i]["ID"].asString();
            lunGroupMO.m_name = data[i]["NAME"].asString();
            DBGLOG("Lun group info: %s %s", lunGroupMO.m_name.c_str(), lunGroupMO.m_id.c_str());
            return Module::SUCCESS;
        }
        ERRLOG("Get lun group by name failed, not find.");
        return Module::FAILED;
    }
    ERRLOG("Query lun group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::CreateHostGroup(const std::string &hostGroupName, HostGroupMO &hostGroupMO, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "POST";
    req.url = std::string("hostgroup");
    Json::Value jsonBody;
    jsonBody["TYPE"] = MO_HOST_GROUP;
    jsonBody["NAME"] = hostGroupName;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        hostGroupMO.m_id = data["ID"].asString();
        hostGroupMO.m_name = data["NAME"].asString();
        return Module::SUCCESS;
    }
    if (iRet == Module::SUCCESS && errorCode == ApiErrorCode::HOST_GROUP_EXIST) {
        return GetHostGroupByName(hostGroupName, hostGroupMO, errorDes);
    }
    ERRLOG("Create host group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::AddHostToHostGroup(const std::string& hostID, const std::string& hostGroupID,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "POST";
    req.url = std::string("hostgroup/associate");
    Json::Value jsonBody;
    jsonBody["ID"] = hostGroupID;
    jsonBody["ASSOCIATEOBJTYPE"] = MO_HOST;
    jsonBody["ASSOCIATEOBJID"] = hostID;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS ||
        errorCode == ApiErrorCode::HOST_IS_ALREADY_IN_HOSTGRP)) {
        return Module::SUCCESS;
    }
    ERRLOG("Add host to host group failed! reason: %s", errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetHostGroupByName(const std::string &hostGroupName, HostGroupMO &hostGroupMO,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "hostgroup?filter=NAME::" + hostGroupName;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty() && data.isArray()) {
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i]["NAME"].asString() != hostGroupName) {
                continue;
            }
            hostGroupMO.m_id = data[i]["ID"].asString();
            hostGroupMO.m_name = data[i]["NAME"].asString();
            DBGLOG("Host group info: %s %s", hostGroupMO.m_name.c_str(), hostGroupMO.m_id.c_str());
            return Module::SUCCESS;
        }
        ERRLOG("Get host group by name failed, not find.");
        return Module::FAILED;
    }
    ERRLOG("Query host group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetAllHostIDFromHostGroup(const std::string &hostgroupID, std::vector<std::string> &hostIDs,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("host/associate?TYPE=21&ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=" + hostgroupID);
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet != Module::SUCCESS || errorCode != Module::SUCCESS) {
        ERRLOG("Get host group failed, error:%d, des:%s", errorCode, errorDes.c_str());
        return (errorCode == 0) ? Module::FAILED : errorCode;
    }
    std::string strHostID;
    for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
        try {
            if (data[i].isMember("ID")) {
                strHostID = data[i]["ID"].asString();
                hostIDs.push_back(strHostID);
            }
        } catch (std::exception &e) {
            ERRLOG("Throw a exception.%s",  WIPE_SENSITIVE(e.what()));
            return Module::FAILED;
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::CreateMappingView(const std::string& mappingViewName, MappingViewMO &mappingViewMO,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "POST";
    req.url = std::string("mappingview");
    Json::Value jsonBody;
    jsonBody["TYPE"] = MO_MAPPINGVIEW;
    jsonBody["NAME"] = mappingViewName;
    jsonBody["ENABLEINBANDCOMMAND"] = false;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        mappingViewMO.m_id = data["ID"].asString();
        mappingViewMO.m_name = data["NAME"].asString();
        return Module::SUCCESS;
    }
    ERRLOG("Create mapping view failed, des: %s", errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetMappingViewByName(const std::string &mapViewName, MappingViewMO& mappingMO,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = "mappingview?filter=NAME::" + mapViewName;
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty() && data.isArray()) {
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i]["NAME"].asString() != mapViewName) {
                continue;
            }
            mappingMO.m_id = data[i]["ID"].asString();
            mappingMO.m_name = data[i]["NAME"].asString();
            mappingMO.m_lunGroupName = data[i]["lunGroupName"].asString();
            mappingMO.m_lunGroupID = data[i]["lunGroupId"].asString();
            mappingMO.m_hostGroupName = data[i]["hostGroupName"].asString();
            mappingMO.m_hostGroupID = data[i]["hostGroupId"].asString();
            DBGLOG("Mapping view info: %s %s", mappingMO.m_name.c_str(), mappingMO.m_id.c_str());
            return Module::SUCCESS;
        }
        ERRLOG("Get mappingview by name failed, not find.");
        return Module::FAILED;
    }
    ERRLOG("Query mappingview failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::AddObjToMappingView(const std::string& mapViewID, const std::string& objId, MO_TYPE objType,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "PUT";
    req.url = std::string("mappingview/create_associate");
    Json::Value jsonBody;
    jsonBody["TYPE"] = MO_MAPPINGVIEW;
    jsonBody["ID"] = mapViewID;
    jsonBody["ASSOCIATEOBJTYPE"] = objType;
    jsonBody["ASSOCIATEOBJID"] = objId;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS) {
        return Module::SUCCESS;
    }
    if (errorCode == ApiErrorCode::LUN_MAPPED_SAME_HOST || errorCode == ApiErrorCode::RETURN_LMP_MAP_REPEAT_HOST_GROUP) {
        INFOLOG("Hostgroup or lungroup %s has added to same mapping view.", objId.c_str());
        return Module::SUCCESS;
    }
    ERRLOG("Add obj group to mapping view failed! reason: %s", errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::RemoveObjFromMappingView(const std::string& mapViewID, const std::string& objId,
    MO_TYPE objType, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "POST";
    req.url = std::string("mappingview/REMOVE_ASSOCIATE");
    Json::Value jsonBody;
    jsonBody["TYPE"] = "245";
    jsonBody["ID"] = mapViewID;
    jsonBody["ASSOCIATEOBJTYPE"] = std::to_string(objType);
    jsonBody["ASSOCIATEOBJID"] = objId;
    Json::FastWriter jsonWriter;
    req.body = jsonWriter.write(jsonBody);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::HOST_GROUP_NOTIN
        || errorCode == ApiErrorCode::LUN_GROUP_NOTIN)) {
        INFOLOG("Remove host group from mapping success.");
        return Module::SUCCESS;
    }
    ERRLOG("Remove host group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::DeleteMapViewByID(const std::string &mapViewID, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = std::string("mappingview/" + mapViewID);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::MAPVIEW_NOTEXIST
        || errorCode == ApiErrorCode::OBJECT_NOTEXIST)) {
        INFOLOG("Delete map view success.");
        return Module::SUCCESS;
    }
    ERRLOG("Delete map view failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetHostGroupFromMappingView(const std::string &mapViewID, HostGroupMO &hostGroupMO,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("hostgroup/associate/mappingview?TYPE=14&ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=" + mapViewID);
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty() && data.isArray()) {
        std::string objId;
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i].isMember("ID")) {
                hostGroupMO.m_id = data[i]["ID"].asString();
                hostGroupMO.m_name = data[i]["NAME"].asString();
                return Module::SUCCESS;
            }
        }
    }
    ERRLOG("Query host group from mapping view failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::GetLunGroupFromMappingView(const std::string &mapViewID, LunGroupMO &lunGroupMO,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("lungroup/associate/mappingview?TYPE=256&ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=" + mapViewID);
    Json::Value data;
    int errorCode;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty() && data.isArray()) {
        std::string objId;
        for (Json::ArrayIndex i = 0; i < data.size(); ++i) {
            if (data[i].isMember("ID")) {
                lunGroupMO.m_id = data[i]["ID"].asString();
                lunGroupMO.m_name = data[i]["NAME"].asString();
                return Module::SUCCESS;
            }
        }
    }
    ERRLOG("Query lun group from mapping view failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::RemoveHostFromHostGroup(const std::string &hostID, const std::string &hostGroupID,
    std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = std::string("host/associate?TYPE=14&ID=" + hostGroupID + "&ASSOCIATEOBJTYPE=21&ASSOCIATEOBJID=" +
        hostID);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::HOST_NOTIN)) {
        INFOLOG("Remove host from host group success.");
        return Module::SUCCESS;
    }
    ERRLOG("Remove host from host group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::DeleteHostGroupByID(const std::string &hostGroupID, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = std::string("hostgroup/" + hostGroupID);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::HOST_GROUP_NOTEXIST
        || errorCode == ApiErrorCode::OBJECT_NOTEXIST)) {
        INFOLOG("Delete host group success.");
        return Module::SUCCESS;
    }
    ERRLOG("Delete host group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::DeleteLunGroupByID(const std::string &lunGroupID, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = std::string("lungroup/" + lunGroupID);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::LUN_GROUP_NOTEXIST
        || errorCode == ApiErrorCode::OBJECT_NOTEXIST)) {
        INFOLOG("Delete lun group success.");
        return Module::SUCCESS;
    }
    ERRLOG("Delete lun group failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

int32_t ApiOperator::DeleteHostByID(const std::string &hostID, std::string &errorDes)
{
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = std::string("host/" + hostID);
    Json::Value data;
    int errorCode;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::HOST_NOTEXIST
        || errorCode == ApiErrorCode::OBJECT_NOTEXIST)) {
        INFOLOG("Delete host success.");
        return Module::SUCCESS;
    }
    ERRLOG("Delete host failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

bool ApiOperator::CheckIscsiIsLogined(const std::vector<std::string> &targetIps)
{
    std::vector<IscsiSessionStatus> sessionStatusList;
    DiskScannerHandler::GetInstance()->GetIscsiSessionStatus(sessionStatusList);
    bool loginFlag = false;
    std::vector<std::string>::const_iterator iter = targetIps.begin();
    for (; iter != targetIps.end(); ++iter) {
        std::string strTemp = *iter;
        loginFlag = CheckContain(strTemp, sessionStatusList);
        if (loginFlag) {
            INFOLOG("Target ip has logined, ip:%s", strTemp.c_str());
            return true;
        }
    }
    INFOLOG("New target ip needed to login!");
    return false;
}

bool ApiOperator::CheckContain(const std::string &strTarget, const std::vector<IscsiSessionStatus> &loginedSessions)
{
    bool bRet = false;
    for (const auto &iter : loginedSessions) {
        if (strTarget == iter.m_targetIp && iter.m_sessionStatus == ISCSI_SESSION_STATE_LOGGED_IN) {
            bRet = true; // contain
            m_iscsiIqnName = iter.m_localIqn;
        }
    }
    return bRet;
}

int32_t ApiOperator::InitMappingNameInfo()
{
    if (m_initMappingNameInfo) {
        INFOLOG("MappingNameInfo has inited.");
        return Module::SUCCESS;
    }

    {
        std::lock_guard<std::mutex> guard(m_mutexSetInfo);
        if (m_initMappingNameInfo) {
            INFOLOG("MappingNameInfo has inited.");
            return Module::SUCCESS;
        }
        m_hostUuid = DiskScannerHandler::GetInstance()->GetHostUuid();
        if (m_hostUuid.empty()) {
            ERRLOG("Get host uuid failed");
            return Module::FAILED;
        }
        m_hostName = "Protect_H_" + m_hostUuid;
        m_hostGroupName = "Protect_HG_" + m_hostUuid;
        m_lunGroupName = "Protect_LG_" + m_hostUuid;
        m_mappingViewName = "Protect_MP_" + m_hostUuid;
        CheckLength(m_hostName);
        CheckLength(m_hostGroupName);
        CheckLength(m_lunGroupName);
        CheckLength(m_mappingViewName);
        m_initMappingNameInfo = true;
    }
    return Module::SUCCESS;
}

void ApiOperator::CheckLength(std::string &input)
{
    if (input.length() > MAX_LENGTH) {
        input = input.substr(0, MAX_LENGTH);
    }
}

int32_t ApiOperator::CheckAndLoginIscsiTarget()
{
    std::vector<std::string> targetIps;
    std::string errorDes;
    if (GetTargetIP(targetIps, errorDes) != Module::SUCCESS) {
        ERRLOG("Get target ip failed, error des:%s", errorDes.c_str());
        return Module::FAILED;
    }
    if (LoginIscsiTarget(targetIps) != Module::SUCCESS) { // 登录后，存储上自动生成本机启动器
        return Module::FAILED;
    }
    m_useIscsi = true;
    return Module::SUCCESS;
}

int32_t ApiOperator::LoginIscsiTarget(std::vector<std::string>& targetIps)
{
    bool loginFlag = false;
    std::vector<IscsiSessionStatus> sessionStatusList;
    DiskScannerHandler::GetInstance()->GetIscsiSessionStatus(sessionStatusList);

    for (const auto &targetIp : targetIps) {
        if (CheckContain(targetIp, sessionStatusList)) {
            DBGLOG("Target ip has logined, ip:%s", targetIp.c_str());
            loginFlag = true;
            continue;
        }
        if (DiskScannerHandler::GetInstance()->LoginIscsiTarget(targetIp) != Module::SUCCESS) {
            WARNLOG("Login iscsi target failed, target ip:%s", targetIp.c_str());
        } else {
            INFOLOG("Login iscsi target success, target ip:%s", targetIp.c_str());
            loginFlag = true;
        }
    }
    if (!loginFlag) {
        ERRLOG("Login all iscsi target ip failed!");
        return Module::FAILED;
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::CreateIscsiInitAndHost(std::string& hostName, HostMO& hostMO, std::string& iqnNumber)
{
    if (m_iscsiIqnName.empty() && DiskScannerHandler::GetInstance()->GetIscsiInitor(m_iscsiIqnName)
        != Module::SUCCESS) {
        ERRLOG("Get iscsi initiator iqn failed!");
        return Module::FAILED;
    }
    IscsiInitiatorMO oRtnIscsiInitiatorMO;
    std::string errorDes;
    if (GetIscsiInitiatorByID(m_iscsiIqnName, oRtnIscsiInitiatorMO, errorDes) != Module::SUCCESS
        || oRtnIscsiInitiatorMO.m_id.empty()) {
        if (CreateIscsiInitiator(m_iscsiIqnName, errorDes) != Module::SUCCESS) { // 创建Iscsi启动器
            ERRLOG("Create iscsi initiator iqn failed, error des:%s", errorDes.c_str());
            return Module::FAILED;
        }
    }
    {
        std::lock_guard<std::mutex> guard(m_mutexHost);
        if (GetHostByName(hostName, hostMO, errorDes) != Module::SUCCESS || hostMO.m_id.empty()) {
            std::string hostIp; // 主机IP地址（默认为空）
            if (CreateHost(hostName, hostIp, hostMO, errorDes) != Module::SUCCESS) { // 创建主机
                ERRLOG("Create host failed, %s", errorDes.c_str());
                return Module::FAILED;
            }
        }
    }
    if (oRtnIscsiInitiatorMO.m_parentName.empty()) {
        if (AddIscsiInitiatorToHost(m_iscsiIqnName, hostMO.m_id, errorDes) != Module::SUCCESS) { // 添加启动器到主机
            ERRLOG("Add iscsi initiator to host failed, %s", errorDes.c_str());
            return Module::FAILED;
        }
    } else if (!oRtnIscsiInitiatorMO.m_parentName.empty() && oRtnIscsiInitiatorMO.m_parentName != hostName) {
        hostName = oRtnIscsiInitiatorMO.m_parentName; // 本机启动器已被映射至其他主机，使用该主机
        hostMO.m_id = oRtnIscsiInitiatorMO.m_parentID;
        hostMO.m_name = oRtnIscsiInitiatorMO.m_parentName;
        WARNLOG("Use iscsi initiator existing host id:%s, name:%s", hostMO.m_id.c_str(), hostMO.m_name.c_str());
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::CreateFcInitAndHost(std::string& hostName, HostMO& hostMO)
{
    std::string errorDes;
    {
        std::lock_guard<std::mutex> guard(m_mutexHost);
        if (GetHostByName(hostName, hostMO, errorDes) != Module::SUCCESS || hostMO.m_id.empty()) {
            std::string hostIp; // 主机IP地址（默认为空）
            if (CreateHost(hostName, hostIp, hostMO, errorDes) != Module::SUCCESS) { // 创建主机
                ERRLOG("Create host failed, %s", errorDes.c_str());
                return Module::FAILED;
            }
        }
    }
    std::vector<std::string> vecFcWwpn;
    if (DiskScannerHandler::GetInstance()->GetFCInitor(vecFcWwpn) != Module::SUCCESS) {
        return Module::FAILED;
    }
    bool createFcInitAndOnline = false;
    for (std::vector<std::string>::iterator iter = vecFcWwpn.begin(); iter != vecFcWwpn.end(); ++iter) {
        FCInitiatorMO fcMO;
        if (GetFcInitiatorByID(*iter, fcMO, errorDes) != Module::SUCCESS) { // FC启动器不存在
            if (CreateFcInitiator(*iter, fcMO, errorDes) != Module::SUCCESS) { // 创建FC启动器
                return Module::FAILED;
            }
        }
        if (fcMO.m_runningStatus != RUN_ONLINE) { // FC启动器存在，但不在线，忽略该启动器
            continue;
        }
        if (fcMO.m_parentName.empty() && AddFcInitiatorToHost(*iter, hostMO.m_id, errorDes) != Module::SUCCESS) {
            // FC启动器存在，且在线，但无关联主机，添加启动器到主机
            ERRLOG("Add fc initiator to host failed, %s", errorDes.c_str());
            return Module::FAILED;
        } else if (!fcMO.m_parentName.empty() && fcMO.m_parentName != hostName) { // 启动器已被映射至其他主机，使用该主机
            hostName = fcMO.m_parentName;
            hostMO.m_id = fcMO.m_parentID;
            hostMO.m_name = fcMO.m_parentName;
            WARNLOG("Use fc initiator existing host:%s", hostName.c_str());
        }
        createFcInitAndOnline = true;
        m_useIscsi = false;
    }
    if (!createFcInitAndOnline) {
        ERRLOG("Not found online fc initiator.");
    }
    return createFcInitAndOnline ? Module::SUCCESS : Module::FAILED;
}

int32_t ApiOperator::CreateInitiatorAndHost(std::string& hostName, HostMO& hostMO)
{
    std::lock_guard<std::mutex> guard(m_mutexInitiator);
    if (CreateFcInitAndHost(hostName, hostMO) == Module::SUCCESS) {
        return Module::SUCCESS;
    }
    if (CheckAndLoginIscsiTarget() != Module::SUCCESS) {
        return Module::FAILED;
    }
    std::string iqnNumber;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(iqnNumber); });
    if (CreateIscsiInitAndHost(hostName, hostMO, iqnNumber) != Module::SUCCESS) {
        return Module::FAILED;
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::CreateLunGroupAndAddObj(const std::string& lunGroupName, const std::string& objId, MO_TYPE objType)
{
    LunGroupMO lunGroupMO;
    std::string errorDes;
    int32_t iRet;
    {
        std::lock_guard<std::mutex> guard(m_mutexLunGroup);
        iRet = GetLunGroupByName(lunGroupName, lunGroupMO, errorDes);
        if (iRet != Module::SUCCESS || lunGroupMO.m_id.empty()) {
            if (CreateLunGroup(lunGroupName, lunGroupMO, errorDes) != Module::SUCCESS) { // 创建LUN组
                ERRLOG("Create lun group failed, %s", errorDes.c_str());
                return Module::FAILED;
            }
        }
    }
    m_lunGroupID = lunGroupMO.m_id;
    std::vector<std::string> objIds;
    bool addLunToLunGroupFlag = false;
    iRet = AssocGetObjIdsByLunGroupID(lunGroupMO.m_id, objType, objIds, errorDes); // 查询LUN组中的LUN（快照）
    if (iRet != Module::SUCCESS || objIds.size() == 0) {
        addLunToLunGroupFlag = true;
    }
    if (objIds.size() > 0) {
        auto iter = std::find(std::begin(objIds), std::end(objIds), objId); // 判断目标LUN（快照）是否已在LUN组中
        if (iter == std::end(objIds)) {
            addLunToLunGroupFlag = true;
        }
    }
    if (addLunToLunGroupFlag) {
        // 映射LUN（快照）到lun组前，先取消其他lun组的映射
        RemovedObjFromAllLunGroup(objId, objType, errorDes);
        if (AddObjToLunGroup(lunGroupMO.m_id, objId, objType, errorDes) != Module::SUCCESS) {
            ERRLOG("Add obj to lun group failed, %s", errorDes.c_str());
            return Module::FAILED;
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::CreateHostGroupAndAddHost(const std::string& hostGroupName, const std::string& hostId)
{
    HostGroupMO hostGroupMO;
    std::string errorDes;
    int32_t iRet;
    {
        std::lock_guard<std::mutex> guard(m_mutexHostGroup);
        iRet = GetHostGroupByName(hostGroupName, hostGroupMO, errorDes);
        if (iRet != Module::SUCCESS || hostGroupMO.m_id.empty()) {
            if (CreateHostGroup(hostGroupName, hostGroupMO, errorDes) != Module::SUCCESS) { // 创建Host组
                ERRLOG("Create host group failed, %s", errorDes.c_str());
                return Module::FAILED;
            }
        }
    }
    m_hostGroupID = hostGroupMO.m_id;
    std::vector<std::string> hostIds;
    bool addHostToHostGroupFlag = false;
    iRet = GetAllHostIDFromHostGroup(hostGroupMO.m_id, hostIds, errorDes); // 查询Host组中的Host
    if (iRet != Module::SUCCESS || hostIds.size() == 0) {
        addHostToHostGroupFlag = true;
    }
    if (hostIds.size() > 0) {
        auto iter = std::find(std::begin(hostIds), std::end(hostIds), hostId); // 判断目标Host是否已在Host组中
        if (iter == std::end(hostIds)) {
            addHostToHostGroupFlag = true;
        }
    }
    if (addHostToHostGroupFlag) {
        if (AddHostToHostGroup(hostId, hostGroupMO.m_id, errorDes) != Module::SUCCESS) {
            ERRLOG("Add host to host group failed, %s", errorDes.c_str());
            return Module::FAILED;
        }
    }
    return Module::SUCCESS;
}

int32_t ApiOperator::RemoveObjFromLunGroupExp(const std::string& lunGroupName, const std::string& objId,
    MO_TYPE objType)
{
    INFOLOG("Start to remove obj from lun group, obj id:%s", objId.c_str());
    if (lunGroupName.empty() || objId.empty()) {
        INFOLOG("Lun group name or lun id is empty, no need to remove, lun group:%s, obj id:%s",
            lunGroupName.c_str(), objId.c_str());
        return Module::SUCCESS;
    }
    std::string errorDes;
    LunGroupMO lunGroupMO;
    int32_t iRet = GetLunGroupByName(lunGroupName, lunGroupMO, errorDes);
    if (iRet != Module::SUCCESS || lunGroupMO.m_id.empty()) {
        DBGLOG("Lun group is empty, no need to remove");
        return Module::SUCCESS;
    }
    iRet = RemoveObjFromLunGroup(lunGroupMO.m_id, objId, objType, errorDes);
    if (iRet != Module::SUCCESS) {
        ERRLOG("Remove obj from lun group failed, %s", errorDes.c_str());
        return iRet;
    }
    INFOLOG("Remove obj from lun group success, obj id:%s", objId.c_str());
    return Module::SUCCESS;
}

std::string ApiOperator::FormatFullUrl(const std::string& fullUrl)
{
    std::string baseUrl = "";
    std::string headUrl = "";

    int baseUrloffset = 3;
    std::string::size_type head = fullUrl.find("://");
    if (head != std::string::npos) {
        baseUrl = fullUrl.substr(head + baseUrloffset);
        headUrl = fullUrl.substr(0, head + baseUrloffset);
    } else {
        return fullUrl;
    }

    std::string::size_type checkend = fullUrl.find('/', head + baseUrloffset);
    if (checkend == std::string::npos) {
        return fullUrl;
    }
    std::string iPAndPortString = fullUrl.substr(head + baseUrloffset, checkend - head - baseUrloffset);

    int colonsCnt = 2;
    unsigned int count = 0;
    for (unsigned int i = 0; i < iPAndPortString.length(); i++) {
        if (iPAndPortString[i] == ':') {
            count++;
            if (count >= colonsCnt) {
                break;
            }
        }
    }
    if (count <= 1) {
        // this is IPV4
        return fullUrl;
    } else {
        // this is IPV6
        if (iPAndPortString.find('[') != std::string::npos) {
            return fullUrl;
        }
        std::string::size_type lastDelim = iPAndPortString.find_last_of(':');
        std::string ipstr = baseUrl.substr(0, lastDelim);
        std::string portstr = baseUrl.substr(lastDelim + 1);
        std::string ipv6str = headUrl + "[" + ipstr + "]" + ":" + portstr;
        DBGLOG("new URL=%s", ipv6str.c_str());
        return ipv6str;
    }
}

int32_t ApiOperator::TestDeviceConnection()
{
    int32_t ret = Module::FAILED;
    std::string des;
    StorageSessionInfo sessionInfo;
    if (!m_useCache) {
        sessionInfo = Login(des);
        if (sessionInfo.deviceId.empty() || sessionInfo.token.empty() || sessionInfo.cookie.empty()) {
            ERRLOG("Login storage failed, deviceId: %s", sessionInfo.deviceId.c_str());
            return Module::FAILED;
        }
        m_sessionPtr = std::make_shared<StorageSession>(sessionInfo.token, sessionInfo.deviceId,
                                                                sessionInfo.cookie);
    }
    std::vector<std::string> vecFcWwpn;
    if (TestIscsiTargetIpConnect() == Module::SUCCESS ||
        TestFcConnect() == Module::SUCCESS) {
        ret = Module::SUCCESS;
    }
    if (!m_useCache && (Logout(sessionInfo) != Module::SUCCESS)) {
        WARNLOG("Connection success, but logout failed");
    } else {
        m_sessionPtr = nullptr;
        DBGLOG("Connection success, and logout also success");
    }
    return ret;
}

int32_t ApiOperator::TestFcConnect()
{
    std::string errorDes;
    std::vector<std::string> vecFcWwpn;
    if (DiskScannerHandler::GetInstance()->GetFCInitor(vecFcWwpn) != Module::SUCCESS) {
        ERRLOG("GetFCInitor failed.");
        return Module::FAILED;
    }
    bool hasOnlineFcInitiator = false;
    for (const std::string &id : vecFcWwpn) {
        FCInitiatorMO fcMO;
        if (GetFcInitiatorByID(id, fcMO, errorDes) != Module::SUCCESS) { // FC启动器不存在
            if (CreateFcInitiator(id, fcMO, errorDes) != Module::SUCCESS) { // 创建FC启动器
                ERRLOG("CreateFcInitiator failed.");
                return Module::FAILED;
            }
        }
        if (fcMO.m_runningStatus != RUN_ONLINE) { // FC启动器存在，但不在线，忽略该启动器
            continue;
        }
        hasOnlineFcInitiator = true;
    }
    if (!hasOnlineFcInitiator) {
        ERRLOG("Not found online fc initiator.");
    }
    return hasOnlineFcInitiator ? Module::SUCCESS : Module::FAILED;
}

int32_t ApiOperator::TestIscsiTargetIpConnect()
{
    int32_t ret = Module::FAILED;
    std::vector<std::string> targetIps;
    std::string errorDes;
    if (GetTargetIP(targetIps, errorDes) != Module::SUCCESS) {
        ERRLOG("Get target ip failed, error des:%s", errorDes.c_str());
    } else {
        for (const auto &targetIp : targetIps) {
            DiskScannerHandler::GetInstance()->AddIscsiLogicIpRoutePolicy(targetIp);
            if (DiskScannerHandler::GetInstance()->TestConnect(targetIp) == Module::SUCCESS) {
                DBGLOG("Test connect target ip success, target ip:%s", targetIp.c_str());
                ret = Module::SUCCESS;
                break;
            }
            WARNLOG("Test connect target ip failed, target ip:%s", targetIp.c_str());
        }
    }
    return ret;
}

int32_t ApiOperator::QueryStoragePoolUsedRate(double &usedCapacityRate)
{
    Module::HttpRequest req;
    req.method = "GET";
    req.url = std::string("storagepool/" + std::to_string(m_deviceInfo.m_poolId));
    Json::Value data;
    int errorCode;
    std::string errorDes;
    int32_t iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty()) {
        if (!data.isMember("USERCONSUMEDCAPACITYPERCENTAGE")) {
            ERRLOG("The response param error, id error.");
            return Module::FAILED;
        }
        int32_t useRate;
        try {
            useRate = std::stoi(data["USERCONSUMEDCAPACITYPERCENTAGE"].asString());
        } catch (std::exception &e) {
            ERRLOG("String to int exception: %s.", WIPE_SENSITIVE(e.what()).c_str());
            return Module::FAILED;
        }
        usedCapacityRate = double(useRate) / double(NUM_100);
        INFOLOG("Query storage pool used rate success.");
        return Module::SUCCESS;
    }
    ERRLOG("Query storage pool used rate failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return (errorCode == 0) ? Module::FAILED : errorCode;
}

void ApiOperator::SetRetry(const bool retry)
{
    m_isRetry = retry;
}

void ApiOperator::SetCurlTimeOut(const uint64_t &curlTimeOut)
{
    m_curlTimeOut = curlTimeOut;
}
}